package com.yuyou.fn.educational.service.impl;

import com.alibaba.fastjson.JSON;
import com.yuyou.fn.common.exception.BusException;
import com.yuyou.fn.common.util.BeanUtils;
import com.yuyou.fn.common.util.Threads;
import com.yuyou.fn.common.util.Utils;
import com.yuyou.fn.educational.discount.*;
import com.yuyou.fn.educational.entity.Discount;
import com.yuyou.fn.educational.entity.Student;
import com.yuyou.fn.educational.service.*;
import com.yuyou.fn.educational.vo.DiscountInfoVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Service
public class DiscountCalculateServiceImpl implements IDiscountCalculateService {

    private final static Logger log = LoggerFactory.getLogger(DiscountCalculateServiceImpl.class);
    
    private final static ExecutorService executorService = Executors.newFixedThreadPool(30,Threads.makeThreadName("discount-cal-"));

    @Resource
    private IDiscountService discountService ;

    @Resource
    private IPeriodService periodService;

    @Resource
    private IStudentRegService studentRegService ;

    @Resource
    private IExternalRegDetailService externalRegDetailService;

    @Override
    public List<DiscountInfoVo> calOfflineDiscount(Long areaTeamId ,final Student student, List<DiscountRegClassInfo> discountRegClassInfoList, List<Long> discountIdsOfPolicy ,Date orderTime ) {
        Map<Long,List<DiscountRegClassInfo>> areaTeamId2RegClassInfoGroup  = BeanUtils.toGroup( discountRegClassInfoList,"areaTeamId" );
        List<Discount> discountList  = discountService.findEffectiveWithoutPolicyByAreaTeamId(areaTeamId, orderTime);

        if(discountIdsOfPolicy != null && !discountIdsOfPolicy.isEmpty() ){
            for(Discount discount : discountService.findByIds(discountIdsOfPolicy)){
                if( discount.getCategoryId().equals(1l) && discount.getEffectiveStartTime().before(orderTime) && discount.getEffectiveEndTime().after(orderTime)){
                    discountList.add(discount);
                }
            }
        }

        CompletionService<DiscountInfoVo> completionService = new ExecutorCompletionService<DiscountInfoVo>(executorService);
        int task = 0 ;
        for(final Discount discount : discountList ){

            final List<DiscountRegClassInfo> list = areaTeamId2RegClassInfoGroup.get(discount.getAreaTeamId());

            completionService.submit(()-> {
                try{
                    return discountCal(student, discount, list);
                }catch (Exception e){
                    log.error("Cal discount fail" , e );
                    return null ;
                }
            });
            task++ ;
        }


        List<DiscountInfoVo> discountInfoList = new ArrayList<>(discountList.size());
        while (task > 0 ){
            try {
                DiscountInfoVo discountInfoVo = completionService.take().get();
                if(discountInfoVo != null ){
                    discountInfoList.add(discountInfoVo);
                }
            } catch (Exception e) {
                //e.printStackTrace();
            }finally {
                task--;
            }
        }
        discountInfoList = filterExclusiveDiscount(discountInfoList);
        return discountInfoList;
    }

    private DiscountInfoVo discountCal(Student student, Discount discount, List<DiscountRegClassInfo> list) {
        list = filter(discount,list) ;



        if(list.isEmpty()){
            return null ;
        }

        List<DiscountRegCourseClass> discountRegCourseClassList = new ArrayList<>(1);
        discountRegCourseClassList.add(new DiscountRegCourseClass(student,list));

        Map<Long,DiscountInfoVo> studentId2DiscountInfoVoMap = cal(discount,discountRegCourseClassList);


        return studentId2DiscountInfoVoMap.get(student.getStudentId());

    }

    @Override
    public Map<Long, List<DiscountInfoVo>> calOnlineDiscount(Long areaTeamId , List<DiscountRegCourseClass> discountRegCourseClassList) {

        List<Discount> discountList = discountService.findEffectiveForNetRegByAreaTeamId(areaTeamId);

        Map<Long, List<DiscountInfoVo>> student2DiscountInfoVoListMap = new HashMap<>();

        CompletionService<Map<Long,DiscountInfoVo>> completionService = new ExecutorCompletionService<>(executorService);
        int task = 0 ;

        for(Discount discount : discountList ){
            completionService.submit(() -> {
                //copy && filter
                List<DiscountRegCourseClass> discountRegCourseClassList2 = new ArrayList<>(1);
                for(DiscountRegCourseClass discountRegCourseClass :discountRegCourseClassList ){
                    List<DiscountRegClassInfo> list = discountRegCourseClass.getDiscountRegClassInfoList();
                    list = filter(discount,list) ;
                    if(list.isEmpty()){
                        continue;
                    }
                    discountRegCourseClassList2.add(new DiscountRegCourseClass(discountRegCourseClass.getStudent() ,list)) ;
                }

                try{
                    return cal(discount, discountRegCourseClassList2);
                }catch (Exception e){
                    log.error("Cal discount fail" , e );
                    return null ;
                }
            });
            task++ ;
        }

        while (task > 0 ){
            try {
                Map<Long, DiscountInfoVo> studentId2DiscountInfoVoMap = completionService.take().get();

                if(student2DiscountInfoVoListMap != null ){
                    for(Map.Entry<Long, DiscountInfoVo> entry : studentId2DiscountInfoVoMap.entrySet() ){
                        List<DiscountInfoVo> discountInfoVoList = student2DiscountInfoVoListMap.get(entry.getKey());
                        if(discountInfoVoList == null ){
                            discountInfoVoList = new ArrayList<>(5);
                            student2DiscountInfoVoListMap.put(entry.getKey(),discountInfoVoList);
                        }
                        discountInfoVoList.add(entry.getValue());
                    }
                }
            } catch (Exception e) {
                //e.printStackTrace();
            }finally {
                task--;
            }
        }


        for(Map.Entry<Long, List<DiscountInfoVo>> entry : student2DiscountInfoVoListMap.entrySet() ){
            entry.setValue(filterExclusiveDiscount(entry.getValue()));
        }

        return student2DiscountInfoVoListMap;
    }



    private Map<Long,DiscountInfoVo> cal(Discount discount , List<DiscountRegCourseClass> discountRegCourseClassList) {
        ExecuteServiceProxy executeServiceProxy = new ExecuteServiceProxy(discount.getCalFunction(), discount.getParams() , new Helper(periodService,studentRegService,externalRegDetailService));
        DiscountResultSet discountResultSet = executeServiceProxy.execute(discountRegCourseClassList);

        Map<Long,DiscountInfoVo> ret = new HashMap<>();

        if(discountResultSet!= null ){
            for(DiscountResult discountResult : discountResultSet ){
                DiscountInfoVo discountInfoVo = ret.get(discountResult.getStudentId());
                if(discountInfoVo == null ){
                    discountInfoVo = new DiscountInfoVo() ;
                    discountInfoVo.setClassIds(discountResult.getClassIds());
                    discountInfoVo.setDiscount(discountResult.getDiscount());
                    discountInfoVo.setDiscountId(discount.getDiscountId());
                    discountInfoVo.setDiscountName(discount.getName());
                    discountInfoVo.setDiscountType(discount.getDiscountType());
                    discountInfoVo.setExclusiveDiscountIds(discount.getExclusiveDiscountIds());
                    discountInfoVo.setDiscountRuleId(discount.getDiscountRuleId());
                    discountInfoVo.setPriority(discount.getPriority());
                    if(discount.getDiscountType() ==  0 || discount.getDiscountType() == 1 ){
                        discountInfoVo.setTotalDiscount(discountResult.getDiscount());
                    }else{
                        throw new BusException("Illegal");
                    }
                }else{
                    discountInfoVo.getClassIds().addAll(discountResult.getClassIds());
                    if(discount.getDiscountType() == 1 ){//金额优惠要合并
                        discountInfoVo.setTotalDiscount(discountInfoVo.getTotalDiscount().add( discountResult.getDiscount() ));
                    }
                }
                ret.put(discountResult.getStudentId(),discountInfoVo );
            }
        }
        return ret;
    }

    /**
     * 过滤符合条件的
     * @param discount
     * @param list
     * @return
     */
    private List<DiscountRegClassInfo> filter(Discount discount, List<DiscountRegClassInfo> list) {
        List<DiscountRegClassInfo> newList = new ArrayList<>(list.size());
        for(DiscountRegClassInfo regClassInfo : list ){

            if( !discount.getAreaTeamId().equals(regClassInfo.getAreaTeamId()) ){
                continue;
            }


            if(StringUtils.isNotEmpty(discount.getBusTeamIds()) && !discount.getBusTeamIds().contains(String.valueOf(regClassInfo.getBusTeamId()))){
                continue;
            }

            if(StringUtils.isNotEmpty(discount.getPeriodIds()) && !discount.getPeriodIds().contains(String.valueOf(regClassInfo.getPeriodId()))){
                continue;
            }

            if(StringUtils.isNotEmpty(discount.getProductIds()) && !discount.getProductIds().contains(String.valueOf(regClassInfo.getProductId()))){
                continue;
            }

            if( StringUtils.isNotEmpty(discount.getCourseTemplateIds() ) && !discount.getCourseTemplateIds().contains(String.valueOf(regClassInfo.getCourseTemplateId()))){
                continue;
            }

            if(StringUtils.isNotEmpty(discount.getSubjectIds() ) && !discount.getSubjectIds().contains(String.valueOf(regClassInfo.getSubjectId()))){
                continue;
            }

            if(StringUtils.isNotEmpty(discount.getGradeIds() ) && !discount.getGradeIds().contains(String.valueOf(regClassInfo.getGradeId()))){
                continue;
            }

            newList.add(regClassInfo);

        }
        return newList;
    }

    private List<DiscountInfoVo> filterExclusiveDiscount(List<DiscountInfoVo> discountInfoVoList) {

        if(discountInfoVoList == null || discountInfoVoList.isEmpty() ){
            return Collections.EMPTY_LIST ;
        }

        List<DiscountInfoVo> availableDiscountInfoList = new ArrayList<>(discountInfoVoList.size());


        availableDiscountInfoList.addAll(discountInfoVoList);


        Set<String> exclusiveSet = new HashSet<>();
        for(DiscountInfoVo discountInfoVo :  availableDiscountInfoList){
            if(StringUtils.isEmpty(discountInfoVo.getExclusiveDiscountIds())){
                continue;
            }
            String[] exclusiveIds = discountInfoVo.getExclusiveDiscountIds().split(",");
            for(String exclusiveId : exclusiveIds ) {
                exclusiveSet.add(getKey(String.valueOf(discountInfoVo.getDiscountId()) , exclusiveId  ));
            }
        }


        //不能同时叠加的优惠，先扣后折
        Collections.sort(availableDiscountInfoList, new Comparator<DiscountInfoVo>() {
            @Override
            public int compare(DiscountInfoVo o1, DiscountInfoVo o2) {

                //大 -> 小
                int p  = o2.getPriority() - o1.getPriority() ;

                if(p == 0 ){
                    if( o1.getDiscountType() == 1 && o2.getDiscountType() == 1 ){
                        return o2.getTotalDiscount().compareTo(o1.getTotalDiscount());
                    }else if(o1.getDiscountType() == 0 && o2.getDiscountType() == 0 ){
                        return o1.getTotalDiscount().compareTo(o2.getTotalDiscount());
                    }else {
                        return o2.getDiscountType() - o1.getDiscountType() ;
                    }
                }else{
                    return p ;
                }


            }
        });

        List<DiscountInfoVo> willDelList = new ArrayList<>();
        for(int i = 0 ; i < availableDiscountInfoList.size() - 1 ; i++ ){
            DiscountInfoVo big = availableDiscountInfoList.get(i);
            if(willDelList.contains(big)){
                continue;
            }
            for(int ii = i + 1 ; ii < availableDiscountInfoList.size() ; ii++ ){
                DiscountInfoVo small = availableDiscountInfoList.get(ii);
                if(exclusiveSet.contains(getKey(String.valueOf(big.getDiscountId()),String.valueOf(small.getDiscountId())))){
                    willDelList.add(small);
                }
            }
        }
        availableDiscountInfoList.removeAll(willDelList);
        return availableDiscountInfoList;
    }

    private String getKey(String discountId1, String discountId2) {
        return discountId1.compareTo(discountId2) < 0 ? discountId1 + "," +discountId2 :discountId2 +"," + discountId1 ;
    }





}
